package com.autohammal.ui.frame.items;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

/**
 * @author RLRyan
 */
public class ItemDescriptor {

	public static final String PROPERTYEDITORCLASS = "propertyEditorClass";
	public static final String DISPLAYNAME = "displayName";
	public static final String SHORTDESCRIPTION = "shortDescription";
	public static final String CUSTOMIZERCLASS = "customizerClass";
	public static final String ENUMERATIONVALUES = "enumerationValues";
	public static final String ENUMERATIONLIST = "enumerationList";

	/**
	 * Internal use
	 * 
	 * @param s
	 * @return
	 */
	static String capitalize(String s) {
		if (s == null || s.length() == 0) {
			return s;
		}
		char chars[] = s.toCharArray();
		chars[0] = Character.toUpperCase(chars[0]);
		return new String(chars);
	}

	/**
	 * create an item descriptor
	 * 
	 * @param targetClass
	 *            class of the target, for example:
	 *            MyTestGUIActivity.class,
	 * @param propertyName
	 *            the name of the properties: for example: "testFunction"
	 * @param propType
	 *            the type of the property object, for example: Integer.class
	 * @param aobj
	 * @return
	 */
	public static ItemDescriptor createItemDescriptor(Class<?> targetClass,
			String propName, Class<?> propType, Object aobj[]) {
		ItemDescriptor itemDescriptor = null;
		itemDescriptor = new ItemDescriptor(propName, targetClass, propType);
		for (int i = 0; i < aobj.length; i += 2) {
			 String s1 = (String) aobj[i];
			Object obj = aobj[i + 1];
			if (PROPERTYEDITORCLASS.equals(s1)) {
				itemDescriptor.setPropertyEditorClass((Class<?>) obj);
			} else if (DISPLAYNAME.equals(s1)) {
				itemDescriptor.setDisplayName((String) obj);
			} else if (SHORTDESCRIPTION.equals(s1)) {
				itemDescriptor.setShortDescription((String) obj);
			} else {
				itemDescriptor.setValue(s1, obj);
			}
		}
		return itemDescriptor;
	}

	/**
	 * Convenience method which creates a WeakReference. Internal use
	 */
	static Reference<?> createReference(Object obj) {
		return createReference(obj, false);
	}

	/**
	 * Create a Reference wrapper for the object.
	 * 
	 * @param obj
	 *            object that will be wrapped
	 * @param soft
	 *            true if a SoftReference should be created; otherwise Soft
	 * @return a Reference or null if obj is null.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	static Reference<?> createReference(Object obj, boolean soft) {
		Reference<?> ref = null;
		if (obj != null) {
			if (soft) {
				ref = new SoftReference(obj);
			} else {
				ref = new WeakReference(obj);
			}
		}
		return ref;
	}

	/**
	 * Returns an object from a Reference wrapper.
	 * 
	 * @return the Object in a wrapper or null.
	 */
	static Object getObject(Reference<?> ref) {
		return (ref == null) ? null : (Object) ref.get();
	}

	private Reference<?> classRef;
	private String displayName;
	private String name;
	private Reference<?> propertyEditorClassRef;
	private Reference<?> propertyTypeRef;
	private String shortDescription;
	private java.util.Hashtable<String, Object> table;

	/**
	 * ItemDescriptor constructor
	 * 
	 * @param propertyName
	 *            the name of the properties: for example: "testFunction"
	 * @param targetClass
	 *            class of the target activity, for example:
	 *            MyTestGUIActivity.class,
	 * @param propType
	 *            the type of the property object, for example: Integer.class
	 */
	public ItemDescriptor(String propertyName, Class<?> targetClass,
			Class<?> propType) {
		if (targetClass == null) {
			throw new IllegalArgumentException("Target class is null");
		}
		if (propertyName == null || propertyName.length() == 0) {
			throw new IllegalArgumentException("bad property name");
		}
		if (propType == null) {
			throw new IllegalArgumentException("bad property type");
		}
		setName(propertyName);
		setClass0(targetClass);
		setPropertyType(propType);

	}

	/**
	 * Internal use
	 */
	@SuppressWarnings("unused")
	private void addTable(java.util.Hashtable<String, Object> t) {
		if (t == null) {
			return;
		}
		java.util.Enumeration<String> keys = t.keys();
		while (keys.hasMoreElements()) {
			String key = (String) keys.nextElement();
			Object value = t.get(key);
			setValue(key, value);
		}
	}

	/**
	 * Gets an enumeration of the locale-independent names of this feature.
	 * 
	 * @return An enumeration of the locale-independent names of any attributes
	 *         that have been registered with setValue.
	 */
	public java.util.Enumeration<String> attributeNames() {
		if (table == null) {
			table = new java.util.Hashtable<String, Object>();
		}
		return table.keys();
	}

	/**
	 * Internal use
	 */
	Class<?> getClass0() {
		return (Class<?>) getObject(classRef);
	}

	/**
	 * Gets the localized display name of this feature.
	 * 
	 * @return The localized display name for the property/method/event. This
	 *         defaults to the same as its programmatic name from getName.
	 */
	public String getDisplayName() {
		if (displayName == null) {
			return getName();
		}
		return displayName;
	}

	/**
	 * Gets the programmatic name of this feature.
	 * 
	 * @return The programmatic name of the property/method/event
	 */
	public String getName() {
		return name;
	}

	/**
	 * Gets any explicit PropertyEditor Class that has been registered for this
	 * property.
	 * 
	 * @return Any explicit PropertyEditor Class that has been registered for
	 *         this property. Normally this will return "null", indicating that
	 *         no special editor has been registered, so the
	 *         PropertyEditorManager should be used to locate a suitable
	 *         PropertyEditor.
	 */
	public Class<?> getPropertyEditorClass() {
		return (Class<?>) getObject(propertyEditorClassRef);
	}

	/**
	 * Gets the Class object for the property.
	 * 
	 * @return The Java type info for the property. Note that the "Class" object
	 *         may describe a built-in Java type such as "int". The result may
	 *         be "null" if this is an indexed property that does not support
	 *         non-indexed access.
	 */
	public synchronized Class<?> getPropertyType() {
		return (Class<?>) getObject(propertyTypeRef);
	}

	/**
	 * Gets the short description of this feature.
	 * 
	 * @return A localized short description associated with this
	 *         property/method/event. This defaults to be the display name.
	 */
	public String getShortDescription() {
		if (shortDescription == null) {
			return getDisplayName();
		}
		return shortDescription;
	}

	/**
	 * Retrieve a named attribute with this feature.
	 * 
	 * @param attributeName
	 *            The locale-independent name of the attribute
	 * @return The value of the attribute. May be null if the attribute is
	 *         unknown.
	 */
	public Object getValue(String attributeName) {
		if (table == null) {
			return null;
		}
		return table.get(attributeName);
	}

	/**
	 * Package private methods for recreating the weak/soft referent Internal
	 * use
	 */
	void setClass0(Class<?> cls) {
		classRef = createReference(cls);
	}

	/**
	 * Sets the localized display name of this feature.
	 * 
	 * @param displayName
	 *            The localized display name for the property/method/event.
	 */
	public void setDisplayName(String displayName) {
		this.displayName = displayName;
	}

	/**
	 * Sets the programmatic name of this feature.
	 * 
	 * @param name
	 *            The programmatic name of the property/method/event
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Normally PropertyEditors will be found using the PropertyEditorManager.
	 * However if for some reason you want to associate a particular
	 * PropertyEditor with a given property, then you can do it with this
	 * method.
	 * 
	 * @param propertyEditorClass
	 *            The Class for the desired PropertyEditor.
	 */
	public void setPropertyEditorClass(Class<?> propertyEditorClass) {
		propertyEditorClassRef = createReference(propertyEditorClass);
	}

	/**
	 * set class type for the property. Internal use
	 * 
	 * @param type
	 */
	public void setPropertyType(Class<?> type) {
		propertyTypeRef = createReference(type);
	}

	/**
	 * You can associate a short descriptive string with a feature. Normally
	 * these descriptive strings should be less than about 40 characters.
	 * 
	 * @param text
	 *            A (localized) short description to be associated with this
	 *            property/method/event.
	 */
	public void setShortDescription(String text) {
		shortDescription = text;
	}

	/**
	 * Associate a named attribute with this feature.
	 * 
	 * @param attributeName
	 *            The locale-independent name of the attribute
	 * @param value
	 *            The value.
	 */
	public void setValue(String attributeName, Object value) {
		if (table == null) {
			table = new java.util.Hashtable<String, Object>();
		}
		table.put(attributeName, value);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((displayName == null) ? 0 : displayName.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime
				* result
				+ ((shortDescription == null) ? 0 : shortDescription.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ItemDescriptor other = (ItemDescriptor) obj;
		if (displayName == null) {
			if (other.displayName != null)
				return false;
		} else if (!displayName.equals(other.displayName))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (shortDescription == null) {
			if (other.shortDescription != null)
				return false;
		} else if (!shortDescription.equals(other.shortDescription))
			return false;
		return true;
	}
	
	

}
